
//
// 101022 - AH - Created.
// 101023 - AH - Parsing works.
// 101024a- AH - Renamed from XmlTest.
// 101031a- AH - Renamed from MineSpokeXmlTest.
// 101031b- AH - Renamed inner class Case to MineSpokeTestCase.
// 101031c- AH - Makes MineSpokeTestCase top level class.
// 101031d- AH - size() and getTestCase().
// 101103 - AH - H now extends new base class XmlHandler, moved 
//               throwParseException(String) there.
// 101109 - AH - Now parse and set the subcase and metacase MineSpokeTestCase
//               properties.
// 101110 - AH - Moved getMandatoryAttr(String, Attributes) to XmlHandler.
// 101127 - AH - Tags not longer hold by Set<String> by resource but Tags 
//               object.
// 101217 - AH - Resources are now of type Resource, not AttributeObject 
//               directly.
// 110615 - AH - Change related to that Mine no longer got subject property.
//

package org.soekrat.engine.wheel.test.util;

import java.io.CharArrayWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;

import org.soekrat.engine.io.XmlHandler;
import org.soekrat.engine.mine.Mine;
import org.soekrat.engine.mine.Resource;
import org.soekrat.engine.mine.Tags;
import org.soekrat.engine.util.CommandLineParser;
import org.soekrat.engine.visitor.Input;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;

/**
 * <p>
 *   XML based unit test of 
 *   {@link Mine}.
 * </p>
 * @author Arne Halvorsen (AH)
 * @author Laetitia Le Chatton (LLC) 
 */
public final class MineSpokeXmlTestCases 
{
  private final List<MineSpokeTestCase> m_cases=
    new ArrayList<MineSpokeTestCase>();
  
  /**
   * <p>
   *   Constructor.
   * </p>
   * @param f Represents XML file to parse.
   * @throws IOException If fails.
   */
  MineSpokeXmlTestCases(File f) throws IOException
  {
    if (f==null)
    {
      throw new NullPointerException("f");
    }
    
    FileInputStream fis=null;
    try
    {
      fis=new FileInputStream(f);
      parse(fis);
    }
    finally
    {
      if (fis!=null) try { fis.close(); } catch (IOException _){}
    }
  }
  
  /**
   * <p>
   *   Constructor.
   * </p>
   * @param is Stream of XML to parse.
   * @throws IOException If fails.
   */
  MineSpokeXmlTestCases(InputStream is) throws IOException { parse(is); }
  
  // Invoked from constructors to do parsing.
  private void parse(InputStream is) throws IOException
  {
    SAXParserFactory factory=SAXParserFactory.newInstance();
    factory.setNamespaceAware(false);
    factory.setValidating(false);
    try
    {
      H h=new H();
      factory.newSAXParser().parse(is, h);
    }
    catch (ParserConfigurationException pcx){ throw new IOException(pcx); }
    catch (SAXException saxx){ throw new IOException(saxx); }
  }
  
  /**
   * <p>
   *   Gets number of test cases.
   * </p>
   * @return Count.
   */
  int size(){ return m_cases.size(); }
  
  /**
   * <p>
   *   Gets test case.
   * </p>
   * @param idx Index of test case to get.
   * @return Test case.
   * @throws IndexOutOfBoundsException If {@code idx<0 || idx>=size()}.
   */
  public MineSpokeTestCase getCase(int idx){ return m_cases.get(idx); }
  
  @Override
  public String toString(){ return parseResultToString(m_cases); }
  
  // XML handler used.
  private final class H extends XmlHandler
  { 
    private H(){}
    
    public void startElement(String uri, String lName, String qName,
      Attributes attrs) throws SAXException 
    {
      String n=qName;
           if (n.equals("mine-test")) mineTestStart(attrs);
      else if (n.equals("case"))      caseStart(attrs);
      else if (n.equals("mine"))      mineStart(attrs);
      else if (n.equals("resource"))  resourceStart(attrs);
      else if (n.equals("tag"))       tagStart(attrs);
      else if (n.equals("keywords"))  keywordsStart(attrs);
      else if (n.equals("output"))    outputStart(attrs);
      else if (n.equals("oneof"))     oneOfStart(attrs);
      else if (n.equals("all"))       allStart(attrs);
      else if (n.equals("pass"))      passStart(attrs);
      else
      {
        throw new SAXParseException("uknown element : "+n, m_loc);
      }
    }

    public void endElement(String uri, String lName, String qName)
      throws SAXException
    {
      String n=qName;
           if (n.equals("mine-test")) ;
      else if (n.equals("case"))      caseEnd();
      else if (n.equals("mine"))      mineEnd();
      else if (n.equals("resource"))  resourceEnd();
      else if (n.equals("tag"))       ;
      else if (n.equals("keywords"))  keywordsEnd();
      else if (n.equals("output"))    ;
      else if (n.equals("oneof"))     oneOfEnd();
      else if (n.equals("all"))       allEnd();
      else if (n.equals("pass"))      ;
      else
      {
        throw new SAXParseException("uknown element : "+n, m_loc);
      }
    }
    
    public void characters(char[] ch, int start, int length)
      throws SAXException
    {
      m_chars.append(ch, start, length);
    }
    
    ////////////////////////////////////////////////////////////////////////////
    
    private MineSpokeTestCase m_currCase=null;
    
    private Mine m_currMine=null;
    
    private Resource m_currResource=null;
    
    private final Tags m_currTags=new Tags();
    
    private final StringBuilder m_chars=new StringBuilder();
    
    private boolean m_doOneOf=false;
    
    private boolean m_doAll=false;
    
    private void mineTestStart(Attributes attrs) throws SAXException {}
    
    private void caseStart(Attributes attrs) throws SAXException
    {
      String name=getMandatoryAttr("name", attrs);
      m_currCase=new MineSpokeTestCase(name);
      
      String subcase=attrs.getValue("subcase");
      if (subcase!=null) m_currCase.setSubcase(subcase);
      
      String metacase=attrs.getValue("metacase");
      if (metacase!=null) m_currCase.setMetacase(metacase);
    }
    
    private void caseEnd() throws SAXException
    {
      m_cases.add(m_currCase);
      m_currMine=null;
      m_currCase=null;
    }
    
    private void mineStart(Attributes attrs) throws SAXException
    {
      m_currMine=new Mine();  
    }
    
    private void mineEnd()
    {
      m_currCase.setMine(m_currMine);
    }
    
    private void resourceStart(Attributes attrs) throws SAXException
    {
      String name=getMandatoryAttr("name", attrs);
      if (m_doOneOf)
      {
        Resource res=m_currMine.getMatched(name);
        if (res!=null)
        {
          m_currCase.addOneOf(res);
        }
      }
      if (m_doAll)
      {
        Resource res=m_currMine.getMatched(name);
        if (res!=null)
        {
          m_currCase.addAll(res);
        }
      }
      else
      {
        m_currResource=new Resource(name);
      }
    }
    
    private void tagStart(Attributes attrs) throws SAXException
    {
      String name=getMandatoryAttr("name", attrs);
      m_currTags.add(name);
    }
    
    private void resourceEnd() throws SAXException
    {
      if (m_doAll || m_doOneOf) return;
      
      m_currMine.add(m_currResource, m_currTags);
      m_currResource=null;
      m_currTags.clear();
    }
    
    private void keywordsStart(Attributes attrs) throws SAXException
    { 
      m_chars.setLength(0);
    }
    
    private void keywordsEnd() throws SAXException
    {
      String s=m_chars.toString();
      s=(s==null) ? "" : s.trim();
      if (s.length()>0)
      {
        Input keywords=new Input();
        StringTokenizer st=new StringTokenizer(s, ", ");
        while (st.hasMoreTokens()) keywords.add(st.nextToken());
        m_currCase.setKeywords(keywords);
      }
    }
    
    private void outputStart(Attributes attrs) throws SAXException {}
    
    private void oneOfStart(Attributes attrs) throws SAXException
    {
      m_doOneOf=true;  
    }
    
    private void oneOfEnd() throws SAXException
    {
      m_doOneOf=false;
    }
    
    private void allStart(Attributes attrs) throws SAXException
    {
      m_doAll=true;
    }
    
    private void allEnd() throws SAXException
    {
      m_doAll=false;
    }
    
    private void passStart(Attributes attrs) throws SAXException
    {
      m_currCase.pass();
    }

  }
  
  /**
   * <p>
   *   Gets
   *   {@link String} presentation of parsed data.
   * </p>
   * @param cases Parsed data.
   * @return {@code String} presentation.
   */
  private static String parseResultToString(List<MineSpokeTestCase> cases)
  {
    CharArrayWriter caw=new CharArrayWriter();
    PrintWriter pw=new PrintWriter(caw);
    
    for (MineSpokeTestCase acase : cases)
    {
      pw.println(acase);
    }
    
    pw.flush(); 
    return caw.toString();
  }
  
  public static void main(String[] args) 
  {
    try
    {
      CommandLineParser clp=new CommandLineParser(args);
      File f=clp.fileValue("-in", "documents/mine_xml_02_ex.xml");
      MineSpokeXmlTestCases xt=new MineSpokeXmlTestCases(f);
      System.out.println(xt);
    }
    catch (IOException iox)
    {
      iox.printStackTrace();
    }
    
  }
  
}
